Overview
- Introduce
theme() and customizing graphs using ggplot2
- Demonstrate alternative base themes
- Demonstrate how to customize specific elements of
ggplot2 graphs
Customizing the look and feel
ggplot2 includes many default settings that allow you to quickly begin graphing and visualizing data. For rough cuts and quick examinations of your data, these defaults are adequate. For final presentation, you may wish to customize the visual aesthetics of the graph. Here are some important components that are worth learning how to tweak and adjust.
Let’s begin with a scatterplot of Population against Area from the midwest dataset. The point’s color and size vary based on state (categorical) and popdensity (continuous) columns respectively.
The below plot has the essential components such as the title, axis labels, and legend setup nicely. But we can go further in modifying these components to establish a different “look”.
Most of the requirements related to look and feel can be achieved using the theme() function. It accepts a large number of arguments. Type ?theme in the R console and see for yourself, or check out the vignette on ggplot2 themes written by the package author.
# Setup
library(tidyverse)
data("midwest", package = "ggplot2")
# Add plot components
gg <- ggplot(midwest, aes(x = area, y = poptotal,
color = state, size = popdensity)) +
geom_point() +
geom_smooth(se = FALSE) +
xlim(0, 0.1) +
ylim(0, 500000) +
labs(title = "Area vs. Population",
x = "Area",
y = "Population",
caption = "Source: midwest")
# Call plot
gg

Default themes
The default theme in ggplot2 is theme_gray. There are a set of pre-made themes you can use which are cohesive themes that don’t require modifying individual elements.








I personally prefer to use theme_bw() as it provides excellent contrast when viewed using a projector or large LCD screen, so let’s set that as the default theme and change gg to use theme_bw().
theme_set(theme_bw())
gg <- gg +
theme_bw()
gg

Modifying specific elements
The arguments passed to theme() components must be set using special element_type() functions. There are 4 major types.
element_text() - since the title, subtitle and captions are textual items, element_text() function is used to set it.
element_line() - likewise element_line() is used to modify line based components such as the axis lines, major and minor grid lines, etc.
element_rect() - modifies rectangle components such as plot and panel background.
element_blank() - turns off displaying the theme item.
Let’s examine a number of tasks related to changing the plot output, starting with modifying the title and axis texts.
Adding plot and axis titles
Plot and axis titles and the axis text are part of the plot’s theme. Therefore, it can be modified using the theme() function. The theme() function accepts one of the four element_type() functions mentioned above as arguments. Since the plot and axis titles are textual components, element_text() is used to modify them.
Below, I have changed the size, color, face, and line-height. The axis text can be rotated by changing the angle.
# Modify theme components
gg +
theme(
# title
plot.title = element_text(
size = 20,
face = "bold",
family = "American Typewriter",
color = "tomato",
hjust = 0.5,
lineheight = 1.2
),
# subtitle
plot.subtitle = element_text(
size = 15,
family = "American Typewriter",
face = "bold",
hjust = 0.5
),
# caption
plot.caption = element_text(size = 15),
# X axis title
axis.title.x = element_text(vjust = 10,
size = 15),
# Y axis title
axis.title.y = element_text(size = 15),
# X axis text
axis.text.x = element_text(
size = 10,
angle = 30,
vjust = .5
),
# Y axis text
axis.text.y = element_text(size = 10)
)

vjust, controls the vertical spacing between title (or label) and plot.
hjust, controls the horizontal spacing. Setting it to 0.5 centers the title.
family, is used to set a new font
face, sets the font face (“plain”, “italic”, “bold”, “bold.italic”)
This example covers just some of the potential modifications. Use ?theme to look at the full list of components you can modify.
Modifying the legend
Whenever your plot’s geom_ (like points, lines, bars, etc) is set to change the aesthetics (fill, size, col, shape, or stroke) based on another column, as in geom_point(aes(color = state, size = popdensity)), a legend is automatically drawn.
If you are creating a geom where the aesthetics are static, a legend is not drawn by default. The below examples are for cases where you have the legend created automatically.
How to change a legend title
We have two legends, one each for color and size. The size is based on a continuous variable while the color is based on a categorical (discrete) variable.
There are 3 ways to change the legend title.
Method 1: Using labs()
gg +
labs(color = "State",
size = "Density")

Method 2: Using guides()
gg +
guides(color = guide_legend("State"),
size = guide_legend("Density"))

How to change legend labels and point colors for categories
This can be done using the respective scale_aesthetic_manual() function. The new legend labels are supplied as a character vector to the labels argument. If you want to change the color of the categories, it can be assigned to the values argument as shown in below example.
gg +
scale_color_manual(name = "State",
labels = c("Illinois",
"Indiana",
"Michigan",
"Ohio",
"Wisconsin"),
values = c("IL" = "blue",
"IN" = "red",
"MI" = "green",
"OH" = "brown",
"WI" = "orange"))

Change the order of the legend
In case you want to show the legend for color (State) before size (Density), it can be done with the guides() function. The order of the legend has to be set as desired.
If you want to change the position of the labels inside the legend, set it in the required order as seen in previous example.
gg +
guides(color = guide_legend(order = 2),
size = guide_legend(order = 1))

How to style the legend title, text, and key
The styling of legend title, text, key and the guide can also be adjusted. The legend’s key is a figure-like element, so it has to be set using element_rect() function.
gg +
theme(
legend.title = element_text(size = 12, color = "firebrick"),
legend.text = element_text(size = 10),
legend.key = element_rect(fill = 'springgreen')
) +
guides(color = guide_legend(override.aes = list(size = 2, stroke = 1.5)))

How to remove the legend and change the legend position
The legend’s position inside the plot is an aspect of the theme, so it can be modified using the theme() function. If you want to place the legend inside the plot, you can additionally control the hinge point of the legend using legend.justification.
The legend.position is the x and y axis position in the chart area, where (0,0) is bottom left of the chart and (1,1) is top right. Likewise, legend.justification refers to the hinge point inside the legend.
# No legend
gg + theme(legend.position = "None") +
labs(subtitle = "No Legend")

# Legend to the left
gg + theme(legend.position = "left") +
labs(subtitle = "Legend on the Left")

# legend at the bottom and horizontal
gg + theme(legend.position = "bottom", legend.box = "horizontal") +
labs(subtitle = "Legend at Bottom")

# legend at bottom-right, inside the plot
gg + theme(
legend.title = element_text(size = 12, color = "salmon", face = "bold"),
legend.justification = c(1, 0),
legend.position = c(0.95, 0.05),
legend.background = element_blank(),
legend.key = element_blank()
) +
labs(subtitle = "Legend: Bottom-Right Inside the Plot")

# legend at top-left, inside the plot
gg + theme(
legend.title = element_text(size = 12, color = "salmon", face = "bold"),
legend.justification = c(0, 1),
legend.position = c(0.05, 0.95),
legend.background = element_blank(),
legend.key = element_blank()
) +
labs(subtitle = "Legend: Top-Left Inside the Plot")

Adding text, label, and annotation
How to add text and labels around the points
Let’s try adding some text. We will add text to only those counties that have population greater than 400K. In order to achieve this, I create a subsetted dataframe (midwest_sub) that contains only the counties that meet the condition above. Then, we draw the geom_text and geom_label with this new dataframe as the data source. This will ensure that labels (geom_label) are added only for the points contained in the new dataframe.
# Filter required rows.
midwest_sub <- midwest %>%
filter(poptotal > 400000) %>%
mutate(large_county = ifelse(poptotal > 300000, county, ""))
# Plot text and label
## Text
gg +
geom_text(aes(label = large_county), size = 2, data = midwest_sub) +
labs(subtitle = "With ggplot2::geom_text") +
theme(legend.position = "None")

## Label
gg +
geom_label(
aes(label = large_county),
size = 2,
data = midwest_sub,
alpha = 0.25
) +
labs(subtitle = "With ggplot2::geom_label") +
theme(legend.position = "None")

# Plot text and label that REPELS each other
library(ggrepel)
## Text
gg +
geom_text_repel(aes(label = large_county), size = 2, data = midwest_sub) +
labs(subtitle = "With ggrepel::geom_text_repel") +
theme(legend.position = "None")

## Repel
gg +
geom_label_repel(aes(label = large_county), size = 2, data = midwest_sub) +
labs(subtitle = "With ggrepel::geom_label_repel") +
theme(legend.position = "None")

Since the label is looked up from a different data frame, we need to set the data argument in the new geom_ calls.
How to add annotations anywhere inside the plot
Let’s see how to add annotation to any specific point of the chart. It can be done with the annotate() function. For instance, to add text to the graph we use a “text” geom and give it a label containing the text we want to print on the graph. All we need to do then is specify the x and y coordinates on the graph where it should go.
gg +
annotate("text", x = 0.075, y = 300000, label = "This text is amazing!")

Flipping and reversing the X and Y axis
How to flip the X and Y axis
Just add coord_flip():

How to reverse the scale of an axis
Use scale_x_reverse() for X axis and scale_y_reverse() for Y axis.
gg +
scale_x_reverse() +
scale_y_reverse()

Modifying plot background, major and minor axis
How to change the plot background
# change plot background elements
g +
theme(
panel.background = element_rect(fill = 'khaki'),
panel.grid.major = element_line(color = "burlywood", size = 1.5),
panel.grid.minor = element_line(
colour = "tomato",
size = .25,
linetype = "dashed"
),
panel.border = element_blank(),
axis.line.x = element_line(
colour = "darkorange",
size = 1.5,
lineend = "butt"
),
axis.line.y = element_line(color = "darkorange",
size = 1.5)
) +
labs(title = "Modified Background",
subtitle = "How to Change Major and Minor grid, Axis Lines, No Border")

# change plot margins
g +
theme(plot.background = element_rect(fill = "salmon"),
plot.margin = unit(c(2, 2, 1, 1), "cm")) + # top, right, bottom, left
labs(title = "Modified Background",
subtitle = "How to Change Plot Margin")

Remove major and minor grid, change border, axis title, text, and ticks
By using the element_blank() type, we can turn off any or all of these components.
g +
theme(
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
panel.border = element_blank(),
axis.title = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
) +
labs(title = "Modified Background",
subtitle = "How to remove major and minor axis grid, border, axis title, text and ticks")

Session Info
## setting value
## version R version 3.5.1 (2018-07-02)
## system x86_64, darwin15.6.0
## ui X11
## language (EN)
## collate en_US.UTF-8
## tz America/Chicago
## date 2019-01-02
##
## package * version date source
## backports 1.1.2 2017-12-13 CRAN (R 3.5.0)
## base * 3.5.1 2018-07-05 local
## base64enc 0.1-3 2015-07-28 CRAN (R 3.5.0)
## compiler 3.5.1 2018-07-05 local
## datasets * 3.5.1 2018-07-05 local
## devtools 1.13.6 2018-06-27 CRAN (R 3.5.0)
## digest 0.6.18 2018-10-10 cran (@0.6.18)
## evaluate 0.11 2018-07-17 CRAN (R 3.5.0)
## graphics * 3.5.1 2018-07-05 local
## grDevices * 3.5.1 2018-07-05 local
## htmltools 0.3.6 2017-04-28 CRAN (R 3.5.0)
## knitr 1.20 2018-02-20 CRAN (R 3.5.0)
## magrittr 1.5 2014-11-22 CRAN (R 3.5.0)
## memoise 1.1.0 2017-04-21 CRAN (R 3.5.0)
## methods * 3.5.1 2018-07-05 local
## Rcpp 1.0.0 2018-11-07 cran (@1.0.0)
## rmarkdown 1.10 2018-06-11 CRAN (R 3.5.0)
## rprojroot 1.3-2 2018-01-03 CRAN (R 3.5.0)
## stats * 3.5.1 2018-07-05 local
## stringi 1.2.4 2018-07-20 CRAN (R 3.5.0)
## stringr 1.3.1 2018-05-10 CRAN (R 3.5.0)
## tools 3.5.1 2018-07-05 local
## utils * 3.5.1 2018-07-05 local
## withr 2.1.2 2018-03-15 CRAN (R 3.5.0)
## yaml 2.2.0 2018-07-25 CRAN (R 3.5.0)
LS0tCnRpdGxlOiAiR3JhcGhpbmcgdGlwcyBmb3IgYGdncGxvdDJgIChhbmQgbGlmZSkiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgojIE92ZXJ2aWV3CgoqIEludHJvZHVjZSBgdGhlbWUoKWAgYW5kIGN1c3RvbWl6aW5nIGdyYXBocyB1c2luZyBgZ2dwbG90MmAKKiBEZW1vbnN0cmF0ZSBhbHRlcm5hdGl2ZSBiYXNlIHRoZW1lcwoqIERlbW9uc3RyYXRlIGhvdyB0byBjdXN0b21pemUgc3BlY2lmaWMgZWxlbWVudHMgb2YgYGdncGxvdDJgIGdyYXBocwoKIyBDdXN0b21pemluZyB0aGUgbG9vayBhbmQgZmVlbAoKYGdncGxvdDJgIGluY2x1ZGVzIG1hbnkgZGVmYXVsdCBzZXR0aW5ncyB0aGF0IGFsbG93IHlvdSB0byBxdWlja2x5IGJlZ2luIGdyYXBoaW5nIGFuZCB2aXN1YWxpemluZyBkYXRhLiBGb3Igcm91Z2ggY3V0cyBhbmQgcXVpY2sgZXhhbWluYXRpb25zIG9mIHlvdXIgZGF0YSwgdGhlc2UgZGVmYXVsdHMgYXJlIGFkZXF1YXRlLiBGb3IgZmluYWwgcHJlc2VudGF0aW9uLCB5b3UgbWF5IHdpc2ggdG8gY3VzdG9taXplIHRoZSB2aXN1YWwgYWVzdGhldGljcyBvZiB0aGUgZ3JhcGguIEhlcmUgYXJlIHNvbWUgaW1wb3J0YW50IGNvbXBvbmVudHMgdGhhdCBhcmUgd29ydGggbGVhcm5pbmcgaG93IHRvIHR3ZWFrIGFuZCBhZGp1c3QuCgpMZXQncyBiZWdpbiB3aXRoIGEgc2NhdHRlcnBsb3Qgb2YgUG9wdWxhdGlvbiBhZ2FpbnN0IEFyZWEgZnJvbSB0aGUgYG1pZHdlc3RgIGRhdGFzZXQuXltUaGlzIGlzIGluY2x1ZGVkIGFzIGFuIGV4YW1wbGUgZGF0YXNldCBpbiB0aGUgYGdncGxvdDJgIHBhY2thZ2UuIFR5cGUgYD9taWR3ZXN0YCBpbiB5b3VyIGNvbnNvbGUgZm9yIG1vcmUgaW5mb3JtYXRpb24uXSBUaGUgcG9pbnQncyBjb2xvciBhbmQgc2l6ZSB2YXJ5IGJhc2VkIG9uIGBzdGF0ZWAgKGNhdGVnb3JpY2FsKSBhbmQgYHBvcGRlbnNpdHlgIChjb250aW51b3VzKSBjb2x1bW5zIHJlc3BlY3RpdmVseS4KClRoZSBiZWxvdyBwbG90IGhhcyB0aGUgZXNzZW50aWFsIGNvbXBvbmVudHMgc3VjaCBhcyB0aGUgdGl0bGUsIGF4aXMgbGFiZWxzLCBhbmQgbGVnZW5kIHNldHVwIG5pY2VseS4gQnV0IHdlIGNhbiBnbyBmdXJ0aGVyIGluIG1vZGlmeWluZyB0aGVzZSBjb21wb25lbnRzIHRvIGVzdGFibGlzaCBhIGRpZmZlcmVudCAibG9vayIuCgpNb3N0IG9mIHRoZSByZXF1aXJlbWVudHMgcmVsYXRlZCB0byBsb29rIGFuZCBmZWVsIGNhbiBiZSBhY2hpZXZlZCB1c2luZyB0aGUgYHRoZW1lKClgIGZ1bmN0aW9uLiBJdCBhY2NlcHRzIGEgbGFyZ2UgbnVtYmVyIG9mIGFyZ3VtZW50cy4gVHlwZSBgP3RoZW1lYCBpbiB0aGUgUiBjb25zb2xlIGFuZCBzZWUgZm9yIHlvdXJzZWxmLCBvciBjaGVjayBvdXQgdGhlIFt2aWduZXR0ZSBvbiBgZ2dwbG90MmAgdGhlbWVzIHdyaXR0ZW4gYnkgdGhlIHBhY2thZ2UgYXV0aG9yXShodHRwOi8vZG9jcy5nZ3Bsb3QyLm9yZy9kZXYvdmlnbmV0dGVzL3RoZW1lcy5odG1sKS4KCmBgYHtyIGJhc2V9CiMgU2V0dXAKbGlicmFyeSh0aWR5dmVyc2UpCmRhdGEoIm1pZHdlc3QiLCBwYWNrYWdlID0gImdncGxvdDIiKQoKIyBBZGQgcGxvdCBjb21wb25lbnRzCmdnIDwtIGdncGxvdChtaWR3ZXN0LCBhZXMoeCA9IGFyZWEsIHkgPSBwb3B0b3RhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHN0YXRlLCBzaXplID0gcG9wZGVuc2l0eSkpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKwogIHhsaW0oMCwgMC4xKSArCiAgeWxpbSgwLCA1MDAwMDApICsKICBsYWJzKHRpdGxlID0gIkFyZWEgdnMuIFBvcHVsYXRpb24iLAogICAgICAgeCA9ICJBcmVhIiwKICAgICAgIHkgPSAiUG9wdWxhdGlvbiIsCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogbWlkd2VzdCIpCgojIENhbGwgcGxvdApnZwpgYGAKCiMjIERlZmF1bHQgdGhlbWVzCgpUaGUgZGVmYXVsdCB0aGVtZSBpbiBgZ2dwbG90MmAgaXMgYHRoZW1lX2dyYXlgLiBUaGVyZSBhcmUgYSBzZXQgb2YgcHJlLW1hZGUgdGhlbWVzIHlvdSBjYW4gdXNlIHdoaWNoIGFyZSBjb2hlc2l2ZSB0aGVtZXMgdGhhdCBkb24ndCByZXF1aXJlIG1vZGlmeWluZyBpbmRpdmlkdWFsIGVsZW1lbnRzLgoKYGBge3IsIGRlcGVuZHNvbj0nYmFzZSd9CmdnICsgdGhlbWVfZ3JheSgpCmdnICsgdGhlbWVfYncoKQpnZyArIHRoZW1lX2xpbmVkcmF3KCkKZ2cgKyB0aGVtZV9saWdodCgpCmdnICsgdGhlbWVfZGFyaygpCmdnICsgdGhlbWVfbWluaW1hbCgpCmdnICsgdGhlbWVfY2xhc3NpYygpCmdnICsgdGhlbWVfdm9pZCgpCmBgYAoKSSBwZXJzb25hbGx5IHByZWZlciB0byB1c2UgYHRoZW1lX2J3KClgIGFzIGl0IHByb3ZpZGVzIGV4Y2VsbGVudCBjb250cmFzdCB3aGVuIHZpZXdlZCB1c2luZyBhIHByb2plY3RvciBvciBsYXJnZSBMQ0Qgc2NyZWVuLCBzbyBsZXQncyBzZXQgdGhhdCBhcyB0aGUgZGVmYXVsdCB0aGVtZSBhbmQgY2hhbmdlIGBnZ2AgdG8gdXNlIGB0aGVtZV9idygpYC4KCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfYncoKSkKCmdnIDwtIGdnICsKICB0aGVtZV9idygpCmdnCmBgYAoKIyMgTW9kaWZ5aW5nIHNwZWNpZmljIGVsZW1lbnRzCgpUaGUgYXJndW1lbnRzIHBhc3NlZCB0byBgdGhlbWUoKWAgY29tcG9uZW50cyBtdXN0IGJlIHNldCB1c2luZyBzcGVjaWFsIGBlbGVtZW50X3R5cGUoKWAgZnVuY3Rpb25zLiBUaGVyZSBhcmUgNCBtYWpvciB0eXBlcy4KCjEuIGBlbGVtZW50X3RleHQoKWAgLSBzaW5jZSB0aGUgdGl0bGUsIHN1YnRpdGxlIGFuZCBjYXB0aW9ucyBhcmUgdGV4dHVhbCBpdGVtcywgYGVsZW1lbnRfdGV4dCgpYCBmdW5jdGlvbiBpcyB1c2VkIHRvIHNldCBpdC4KMS4gYGVsZW1lbnRfbGluZSgpYCAtIGxpa2V3aXNlIGBlbGVtZW50X2xpbmUoKWAgaXMgdXNlZCB0byBtb2RpZnkgbGluZSBiYXNlZCBjb21wb25lbnRzIHN1Y2ggYXMgdGhlIGF4aXMgbGluZXMsIG1ham9yIGFuZCBtaW5vciBncmlkIGxpbmVzLCBldGMuCjEuIGBlbGVtZW50X3JlY3QoKWAgLSBtb2RpZmllcyByZWN0YW5nbGUgY29tcG9uZW50cyBzdWNoIGFzIHBsb3QgYW5kIHBhbmVsIGJhY2tncm91bmQuCjEuIGBlbGVtZW50X2JsYW5rKClgIC0gdHVybnMgb2ZmIGRpc3BsYXlpbmcgdGhlIHRoZW1lIGl0ZW0uCgpMZXQncyBleGFtaW5lIGEgbnVtYmVyIG9mIHRhc2tzIHJlbGF0ZWQgdG8gY2hhbmdpbmcgdGhlIHBsb3Qgb3V0cHV0LCBzdGFydGluZyB3aXRoIG1vZGlmeWluZyB0aGUgdGl0bGUgYW5kIGF4aXMgdGV4dHMuCgojIEFkZGluZyBwbG90IGFuZCBheGlzIHRpdGxlcwoKUGxvdCBhbmQgYXhpcyB0aXRsZXMgYW5kIHRoZSBheGlzIHRleHQgYXJlIHBhcnQgb2YgdGhlIHBsb3QncyB0aGVtZS4gVGhlcmVmb3JlLCBpdCBjYW4gYmUgbW9kaWZpZWQgdXNpbmcgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbi4gVGhlIGB0aGVtZSgpYCBmdW5jdGlvbiBhY2NlcHRzIG9uZSBvZiB0aGUgZm91ciBgZWxlbWVudF90eXBlKClgIGZ1bmN0aW9ucyBtZW50aW9uZWQgYWJvdmUgYXMgYXJndW1lbnRzLiBTaW5jZSB0aGUgcGxvdCBhbmQgYXhpcyB0aXRsZXMgYXJlIHRleHR1YWwgY29tcG9uZW50cywgYGVsZW1lbnRfdGV4dCgpYCBpcyB1c2VkIHRvIG1vZGlmeSB0aGVtLgoKQmVsb3csIEkgaGF2ZSBjaGFuZ2VkIHRoZSBzaXplLCBjb2xvciwgZmFjZSwgYW5kIGxpbmUtaGVpZ2h0LiBUaGUgYXhpcyB0ZXh0IGNhbiBiZSByb3RhdGVkIGJ5IGNoYW5naW5nIHRoZSBgYW5nbGVgLgoKYGBge3IsIGRlcGVuZHNvbj0nYmFzZSd9CiMgTW9kaWZ5IHRoZW1lIGNvbXBvbmVudHMKZ2cgKwogIHRoZW1lKAogICAgIyB0aXRsZQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCgKICAgICAgc2l6ZSA9IDIwLAogICAgICBmYWNlID0gImJvbGQiLAogICAgICBmYW1pbHkgPSAiQW1lcmljYW4gVHlwZXdyaXRlciIsCiAgICAgIGNvbG9yID0gInRvbWF0byIsCiAgICAgIGhqdXN0ID0gMC41LAogICAgICBsaW5laGVpZ2h0ID0gMS4yCiAgICApLAogICAgIyBzdWJ0aXRsZQogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dCgKICAgICAgc2l6ZSA9IDE1LAogICAgICBmYW1pbHkgPSAiQW1lcmljYW4gVHlwZXdyaXRlciIsCiAgICAgIGZhY2UgPSAiYm9sZCIsCiAgICAgIGhqdXN0ID0gMC41CiAgICApLAogICAgIyBjYXB0aW9uCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgICMgWCBheGlzIHRpdGxlCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQodmp1c3QgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTUpLAogICAgIyBZIGF4aXMgdGl0bGUKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgIyBYIGF4aXMgdGV4dAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoCiAgICAgIHNpemUgPSAxMCwKICAgICAgYW5nbGUgPSAzMCwKICAgICAgdmp1c3QgPSAuNQogICAgKSwKICAgICMgWSBheGlzIHRleHQKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkKICApCmBgYAoKKiBgdmp1c3RgLCBjb250cm9scyB0aGUgdmVydGljYWwgc3BhY2luZyBiZXR3ZWVuIHRpdGxlIChvciBsYWJlbCkgYW5kIHBsb3QuCiogYGhqdXN0YCwgY29udHJvbHMgdGhlIGhvcml6b250YWwgc3BhY2luZy4gU2V0dGluZyBpdCB0byAwLjUgY2VudGVycyB0aGUgdGl0bGUuCiogYGZhbWlseWAsIGlzIHVzZWQgdG8gc2V0IGEgbmV3IGZvbnQKKiBgZmFjZWAsIHNldHMgdGhlIGZvbnQgZmFjZSAoInBsYWluIiwgIml0YWxpYyIsICJib2xkIiwgImJvbGQuaXRhbGljIikKClRoaXMgZXhhbXBsZSBjb3ZlcnMganVzdCBzb21lIG9mIHRoZSBwb3RlbnRpYWwgbW9kaWZpY2F0aW9ucy4gVXNlIGA/dGhlbWVgIHRvIGxvb2sgYXQgdGhlIGZ1bGwgbGlzdCBvZiBjb21wb25lbnRzIHlvdSBjYW4gbW9kaWZ5LgoKIyBNb2RpZnlpbmcgdGhlIGxlZ2VuZAoKV2hlbmV2ZXIgeW91ciBwbG90J3MgYGdlb21fYCAobGlrZSBwb2ludHMsIGxpbmVzLCBiYXJzLCBldGMpIGlzIHNldCB0byBjaGFuZ2UgdGhlIGFlc3RoZXRpY3MgKGBmaWxsYCwgYHNpemVgLCBgY29sYCwgYHNoYXBlYCwgb3IgYHN0cm9rZWApIGJhc2VkIG9uIGFub3RoZXIgY29sdW1uLCBhcyBpbiBgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBzdGF0ZSwgc2l6ZSA9IHBvcGRlbnNpdHkpKWAsIGEgbGVnZW5kIGlzIGF1dG9tYXRpY2FsbHkgZHJhd24uCgo+IElmIHlvdSBhcmUgY3JlYXRpbmcgYSBnZW9tIHdoZXJlIHRoZSBhZXN0aGV0aWNzIGFyZSBzdGF0aWMsIGEgbGVnZW5kIGlzICoqbm90KiogZHJhd24gYnkgZGVmYXVsdC4gVGhlIGJlbG93IGV4YW1wbGVzIGFyZSBmb3IgY2FzZXMgd2hlcmUgeW91IGhhdmUgdGhlIGxlZ2VuZCBjcmVhdGVkIGF1dG9tYXRpY2FsbHkuCgojIyBIb3cgdG8gY2hhbmdlIGEgbGVnZW5kIHRpdGxlCgpXZSBoYXZlIHR3byBsZWdlbmRzLCBvbmUgZWFjaCBmb3IgY29sb3IgYW5kIHNpemUuIFRoZSBzaXplIGlzIGJhc2VkIG9uIGEgY29udGludW91cyB2YXJpYWJsZSB3aGlsZSB0aGUgY29sb3IgaXMgYmFzZWQgb24gYSBjYXRlZ29yaWNhbCAoZGlzY3JldGUpIHZhcmlhYmxlLiAKClRoZXJlIGFyZSAzIHdheXMgdG8gY2hhbmdlIHRoZSBsZWdlbmQgdGl0bGUuCgojIyMgTWV0aG9kIDE6IFVzaW5nIGBsYWJzKClgCgpgYGB7ciwgZGVwZW5kc29uPSdiYXNlJ30KZ2cgKwogIGxhYnMoY29sb3IgPSAiU3RhdGUiLAogICAgICAgc2l6ZSA9ICJEZW5zaXR5IikKYGBgCgojIyMgTWV0aG9kIDI6IFVzaW5nIGBndWlkZXMoKWAKCmBgYHtyLCBkZXBlbmRzb249J2Jhc2UnfQpnZyArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKCJTdGF0ZSIpLAogICAgICAgICBzaXplID0gZ3VpZGVfbGVnZW5kKCJEZW5zaXR5IikpCmBgYAoKIyMjIE1ldGhvZCAzOiBVc2luZyBgc2NhbGVfYWVzdGhldGljX3ZhcnR5cGUoKWAgZm9ybWF0CgpUaGUgZm9ybWF0IG9mIGBzY2FsZV9hZXN0aGV0aWNfdmFydHlwZSgpYCBhbGxvd3MgeW91IHRvIHR1cm4gb2ZmIHRoZSBsZWdlbmQgZm9yIG9uZSBwYXJ0aWN1bGFyIGFlc3RoZXRpYywgbGVhdmluZyB0aGUgcmVzdCBpbiBwbGFjZS4gVGhpcyBjYW4gYmUgZG9uZSBqdXN0IGJ5IHNldHRpbmcgYGd1aWRlID0gRkFMU0VgLiBGb3IgZXhhbXBsZSwgaWYgdGhlIGxlZ2VuZCBpcyBmb3Igc2l6ZSBvZiBwb2ludHMgYmFzZWQgb24gYSBjb250aW51b3VzIHZhcmlhYmxlLCB0aGVuIGBzY2FsZV9zaXplX2NvbnRpbnVvdXMoKWAgd291bGQgYmUgdGhlIHJpZ2h0IGZ1bmN0aW9uIHRvIHVzZS4gCgpgYGB7ciwgZGVwZW5kc29uPSdiYXNlJ30KZ2cgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiU3RhdGUiKSArCiAgIyB0dXJuIG9mZiBsZWdlbmQgZm9yIHNpemUKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMobmFtZSA9ICJEZW5zaXR5IiwgZ3VpZGUgPSBGQUxTRSkKYGBgCgojIyBIb3cgdG8gY2hhbmdlIGxlZ2VuZCBsYWJlbHMgYW5kIHBvaW50IGNvbG9ycyBmb3IgY2F0ZWdvcmllcwoKVGhpcyBjYW4gYmUgZG9uZSB1c2luZyB0aGUgcmVzcGVjdGl2ZSBgc2NhbGVfYWVzdGhldGljX21hbnVhbCgpYCBmdW5jdGlvbi4gVGhlIG5ldyBsZWdlbmQgbGFiZWxzIGFyZSBzdXBwbGllZCBhcyBhIGNoYXJhY3RlciB2ZWN0b3IgdG8gdGhlIGBsYWJlbHNgIGFyZ3VtZW50LiBJZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBjYXRlZ29yaWVzLCBpdCBjYW4gYmUgYXNzaWduZWQgdG8gdGhlIGB2YWx1ZXNgIGFyZ3VtZW50IGFzIHNob3duIGluIGJlbG93IGV4YW1wbGUuCgpgYGB7ciwgZGVwZW5kc29uPSdiYXNlJ30KZ2cgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIlN0YXRlIiwgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIklsbGlub2lzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkluZGlhbmEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWljaGlnYW4iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT2hpbyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXaXNjb25zaW4iKSwgCiAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIklMIiA9ICJibHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIklOIiA9ICJyZWQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUkiID0gImdyZWVuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9IIiA9ICJicm93biIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXSSIgPSAib3JhbmdlIikpCmBgYAoKIyMgQ2hhbmdlIHRoZSBvcmRlciBvZiB0aGUgbGVnZW5kCgpJbiBjYXNlIHlvdSB3YW50IHRvIHNob3cgdGhlIGxlZ2VuZCBmb3IgY29sb3IgKGBTdGF0ZWApIGJlZm9yZSBzaXplIChgRGVuc2l0eWApLCBpdCBjYW4gYmUgZG9uZSB3aXRoIHRoZSBgZ3VpZGVzKClgIGZ1bmN0aW9uLiBUaGUgYG9yZGVyYCBvZiB0aGUgbGVnZW5kIGhhcyB0byBiZSBzZXQgYXMgZGVzaXJlZC4gCgpJZiB5b3Ugd2FudCB0byBjaGFuZ2UgdGhlIHBvc2l0aW9uIG9mIHRoZSBsYWJlbHMgaW5zaWRlIHRoZSBsZWdlbmQsIHNldCBpdCBpbiB0aGUgcmVxdWlyZWQgb3JkZXIgYXMgc2VlbiBpbiBwcmV2aW91cyBleGFtcGxlLgoKYGBge3IsIGRlcGVuZHNvbj0nYmFzZSd9CmdnICsKICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSwKICAgICAgICAgc2l6ZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpKQpgYGAKCiMjIEhvdyB0byBzdHlsZSB0aGUgbGVnZW5kIHRpdGxlLCB0ZXh0LCBhbmQga2V5CgpUaGUgc3R5bGluZyBvZiBsZWdlbmQgdGl0bGUsIHRleHQsIGtleSBhbmQgdGhlIGd1aWRlIGNhbiBhbHNvIGJlIGFkanVzdGVkLiBUaGUgbGVnZW5kJ3Mga2V5IGlzIGEgZmlndXJlLWxpa2UgZWxlbWVudCwgc28gaXQgaGFzIHRvIGJlIHNldCB1c2luZyBgZWxlbWVudF9yZWN0KClgIGZ1bmN0aW9uLgoKYGBge3IsIGRlcGVuZHNvbj0nYmFzZSd9CmdnICsKICB0aGVtZSgKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGNvbG9yID0gImZpcmVicmljayIpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwKICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICdzcHJpbmdncmVlbicpCiAgKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDIsIHN0cm9rZSA9IDEuNSkpKSAKYGBgCgojIyBIb3cgdG8gcmVtb3ZlIHRoZSBsZWdlbmQgYW5kIGNoYW5nZSB0aGUgbGVnZW5kIHBvc2l0aW9uCgpUaGUgbGVnZW5kJ3MgcG9zaXRpb24gaW5zaWRlIHRoZSBwbG90IGlzIGFuIGFzcGVjdCBvZiB0aGUgdGhlbWUsIHNvIGl0IGNhbiBiZSBtb2RpZmllZCB1c2luZyB0aGUgYHRoZW1lKClgIGZ1bmN0aW9uLiBJZiB5b3Ugd2FudCB0byBwbGFjZSB0aGUgbGVnZW5kIGluc2lkZSB0aGUgcGxvdCwgeW91IGNhbiBhZGRpdGlvbmFsbHkgY29udHJvbCB0aGUgaGluZ2UgcG9pbnQgb2YgdGhlIGxlZ2VuZCB1c2luZyBgbGVnZW5kLmp1c3RpZmljYXRpb25gLgoKVGhlIGBsZWdlbmQucG9zaXRpb25gIGlzIHRoZSB4IGFuZCB5IGF4aXMgcG9zaXRpb24gaW4gdGhlIGNoYXJ0IGFyZWEsIHdoZXJlIGAoMCwwKWAgaXMgYm90dG9tIGxlZnQgb2YgdGhlIGNoYXJ0IGFuZCBgKDEsMSlgIGlzIHRvcCByaWdodC4gTGlrZXdpc2UsIGBsZWdlbmQuanVzdGlmaWNhdGlvbmAgcmVmZXJzIHRvIHRoZSBoaW5nZSBwb2ludCBpbnNpZGUgdGhlIGxlZ2VuZC4KCmBgYHtyLCBkZXBlbmRzb249J2Jhc2UnfQojIE5vIGxlZ2VuZApnZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikgKyAKICBsYWJzKHN1YnRpdGxlID0gIk5vIExlZ2VuZCIpCgojIExlZ2VuZCB0byB0aGUgbGVmdApnZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJsZWZ0IikgKyAKICBsYWJzKHN1YnRpdGxlID0gIkxlZ2VuZCBvbiB0aGUgTGVmdCIpCgojIGxlZ2VuZCBhdCB0aGUgYm90dG9tIGFuZCBob3Jpem9udGFsCmdnICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGxlZ2VuZC5ib3ggPSAiaG9yaXpvbnRhbCIpICsgCiAgbGFicyhzdWJ0aXRsZSA9ICJMZWdlbmQgYXQgQm90dG9tIikKCiMgbGVnZW5kIGF0IGJvdHRvbS1yaWdodCwgaW5zaWRlIHRoZSBwbG90CmdnICsgdGhlbWUoCiAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAic2FsbW9uIiwgZmFjZSA9ICJib2xkIiksCiAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDEsIDApLAogIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC45NSwgMC4wNSksCiAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKQopICsKICBsYWJzKHN1YnRpdGxlID0gIkxlZ2VuZDogQm90dG9tLVJpZ2h0IEluc2lkZSB0aGUgUGxvdCIpCgojIGxlZ2VuZCBhdCB0b3AtbGVmdCwgaW5zaWRlIHRoZSBwbG90CmdnICsgdGhlbWUoCiAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgY29sb3IgPSAic2FsbW9uIiwgZmFjZSA9ICJib2xkIiksCiAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsIDEpLAogIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wNSwgMC45NSksCiAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKQopICsKICBsYWJzKHN1YnRpdGxlID0gIkxlZ2VuZDogVG9wLUxlZnQgSW5zaWRlIHRoZSBQbG90IikKYGBgCgojIEFkZGluZyB0ZXh0LCBsYWJlbCwgYW5kIGFubm90YXRpb24KCiMjIEhvdyB0byBhZGQgdGV4dCBhbmQgbGFiZWxzIGFyb3VuZCB0aGUgcG9pbnRzCgpMZXQncyB0cnkgYWRkaW5nIHNvbWUgdGV4dC4gV2Ugd2lsbCBhZGQgdGV4dCB0byBvbmx5IHRob3NlIGNvdW50aWVzIHRoYXQgaGF2ZSBwb3B1bGF0aW9uIGdyZWF0ZXIgdGhhbiA0MDBLLiBJbiBvcmRlciB0byBhY2hpZXZlIHRoaXMsIEkgY3JlYXRlIGEgc3Vic2V0dGVkIGRhdGFmcmFtZSAoYG1pZHdlc3Rfc3ViYCkgdGhhdCBjb250YWlucyBvbmx5IHRoZSBjb3VudGllcyB0aGF0IG1lZXQgdGhlIGNvbmRpdGlvbiBhYm92ZS4gVGhlbiwgd2UgZHJhdyB0aGUgYGdlb21fdGV4dGAgYW5kIGBnZW9tX2xhYmVsYCB3aXRoIHRoaXMgbmV3IGRhdGFmcmFtZSBhcyB0aGUgYGRhdGFgIHNvdXJjZS4gVGhpcyB3aWxsIGVuc3VyZSB0aGF0IGxhYmVscyAoYGdlb21fbGFiZWxgKSBhcmUgYWRkZWQgb25seSBmb3IgdGhlIHBvaW50cyBjb250YWluZWQgaW4gdGhlIG5ldyBkYXRhZnJhbWUuICAKCmBgYHtyLCBkZXBlbmRzb249J2Jhc2UnfQojIEZpbHRlciByZXF1aXJlZCByb3dzLgptaWR3ZXN0X3N1YiA8LSBtaWR3ZXN0ICU+JQogIGZpbHRlcihwb3B0b3RhbCA+IDQwMDAwMCkgJT4lCiAgbXV0YXRlKGxhcmdlX2NvdW50eSA9IGlmZWxzZShwb3B0b3RhbCA+IDMwMDAwMCwgY291bnR5LCAiIikpCgojIFBsb3QgdGV4dCBhbmQgbGFiZWwKIyMgVGV4dApnZyArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhcmdlX2NvdW50eSksIHNpemUgPSAyLCBkYXRhID0gbWlkd2VzdF9zdWIpICsKICBsYWJzKHN1YnRpdGxlID0gIldpdGggZ2dwbG90Mjo6Z2VvbV90ZXh0IikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikKCiMjIExhYmVsCmdnICsKICBnZW9tX2xhYmVsKAogICAgYWVzKGxhYmVsID0gbGFyZ2VfY291bnR5KSwKICAgIHNpemUgPSAyLAogICAgZGF0YSA9IG1pZHdlc3Rfc3ViLAogICAgYWxwaGEgPSAwLjI1CiAgKSArCiAgbGFicyhzdWJ0aXRsZSA9ICJXaXRoIGdncGxvdDI6Omdlb21fbGFiZWwiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIk5vbmUiKQoKIyBQbG90IHRleHQgYW5kIGxhYmVsIHRoYXQgUkVQRUxTIGVhY2ggb3RoZXIKbGlicmFyeShnZ3JlcGVsKQoKIyMgVGV4dApnZyArCiAgZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGxhcmdlX2NvdW50eSksIHNpemUgPSAyLCBkYXRhID0gbWlkd2VzdF9zdWIpICsKICBsYWJzKHN1YnRpdGxlID0gIldpdGggZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJOb25lIikKCiMjIFJlcGVsCmdnICsKICBnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IGxhcmdlX2NvdW50eSksIHNpemUgPSAyLCBkYXRhID0gbWlkd2VzdF9zdWIpICsKICBsYWJzKHN1YnRpdGxlID0gIldpdGggZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbCIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiTm9uZSIpCmBgYAoKU2luY2UgdGhlIGxhYmVsIGlzIGxvb2tlZCB1cCBmcm9tIGEgZGlmZmVyZW50IGRhdGEgZnJhbWUsIHdlIG5lZWQgdG8gc2V0IHRoZSBkYXRhIGFyZ3VtZW50IGluIHRoZSBuZXcgYGdlb21fYCBjYWxscy4KCiMjIEhvdyB0byBhZGQgYW5ub3RhdGlvbnMgYW55d2hlcmUgaW5zaWRlIHRoZSBwbG90CgpMZXQncyBzZWUgaG93IHRvIGFkZCBhbm5vdGF0aW9uIHRvIGFueSBzcGVjaWZpYyBwb2ludCBvZiB0aGUgY2hhcnQuIEl0IGNhbiBiZSBkb25lIHdpdGggdGhlIGBhbm5vdGF0ZSgpYCBmdW5jdGlvbi4gRm9yIGluc3RhbmNlLCB0byBhZGQgdGV4dCB0byB0aGUgZ3JhcGggd2UgdXNlIGEgInRleHQiIGdlb20gYW5kIGdpdmUgaXQgYSBgbGFiZWxgIGNvbnRhaW5pbmcgdGhlIHRleHQgd2Ugd2FudCB0byBwcmludCBvbiB0aGUgZ3JhcGguIEFsbCB3ZSBuZWVkIHRvIGRvIHRoZW4gaXMgc3BlY2lmeSB0aGUgeCBhbmQgeSBjb29yZGluYXRlcyBvbiB0aGUgZ3JhcGggd2hlcmUgaXQgc2hvdWxkIGdvLgoKYGBge3IsIGRlcGVuZHNvbj0nYmFzZSd9CmdnICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjA3NSwgeSA9IDMwMDAwMCwgbGFiZWwgPSAiVGhpcyB0ZXh0IGlzIGFtYXppbmchIikKYGBgCgojIEZsaXBwaW5nIGFuZCByZXZlcnNpbmcgdGhlIFggYW5kIFkgYXhpcwoKIyMgSG93IHRvIGZsaXAgdGhlIFggYW5kIFkgYXhpcwoKSnVzdCBhZGQgYGNvb3JkX2ZsaXAoKWA6CgpgYGB7ciwgZGVwZW5kc29uPSdiYXNlJ30KZ2cgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCiMjIEhvdyB0byByZXZlcnNlIHRoZSBzY2FsZSBvZiBhbiBheGlzCgpVc2UgYHNjYWxlX3hfcmV2ZXJzZSgpYCBmb3IgWCBheGlzIGFuZCBgc2NhbGVfeV9yZXZlcnNlKClgIGZvciBZIGF4aXMuCgpgYGB7ciwgZGVwZW5kc29uPSdiYXNlJ30KZ2cgKyAKICBzY2FsZV94X3JldmVyc2UoKSArIAogIHNjYWxlX3lfcmV2ZXJzZSgpCmBgYAoKIyBGYWNldGluZzogZHJhdyBtdWx0aXBsZSBwbG90cyB3aXRoaW4gb25lIGZpZ3VyZQoKTGV0J3MgdXNlIHRoZSBgbXBnYCBkYXRhc2V0IGZvciB0aGlzIG9uZS4gSXQgaXMgYXZhaWxhYmxlIGluIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4KCmBgYHtyIG1wZ30KZGF0YShtcGcsIHBhY2thZ2UgPSAiZ2dwbG90MiIpICAjIGxvYWQgZGF0YQoKZyA8LSBnZ3Bsb3QobXBnLCBhZXMoeCA9IGRpc3BsLCB5ID0gaHd5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJod3kgdnMgZGlzcGwiLCBjYXB0aW9uID0gIlNvdXJjZTogbXBnIikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCmcKYGBgCgpXZSBoYXZlIGEgc2ltcGxlIGNoYXJ0IG9mIGhpZ2h3YXkgbWlsZWFnZSBgKGh3eSlgIGFnYWluc3QgdGhlIGVuZ2luZSBkaXNwbGFjZW1lbnQgYChkaXNwbClgIGZvciB0aGUgd2hvbGUgZGF0YXNldC4gQnV0IHdoYXQgaWYgeW91IHdhbnQgdG8gc3R1ZHkgaG93IHRoaXMgcmVsYXRpb25zaGlwIHZhcmllcyBmb3IgZGlmZmVyZW50IGNsYXNzZXMgb2YgdmVoaWNsZXM/CgojIyBGYWNldCB3cmFwCgpgZmFjZXRfd3JhcCgpYCBpcyB1c2VkIHRvIGJyZWFrIGRvd24gYSBsYXJnZSBwbG90IGludG8gbXVsdGlwbGUgc21hbGwgcGxvdHMgZm9yIGluZGl2aWR1YWwgY2F0ZWdvcmllcy4gSXQgdGFrZXMgYSBmb3JtdWxhIGFzIHRoZSBtYWluIGFyZ3VtZW50LiBUaGUgaXRlbXMgdG8gdGhlIGxlZnQgb2YgYH5gIGZvcm1zIHRoZSByb3dzIHdoaWxlIHRob3NlIHRvIHRoZSByaWdodCBmb3JtIHRoZSBjb2x1bW5zLgoKPiBCeSBkZWZhdWx0LCBhbGwgdGhlIHBsb3RzIHNoYXJlIHRoZSBzYW1lIHNjYWxlIGluIGJvdGggWCBhbmQgWSBheGlzLiBZb3UgY2FuIHNldCB0aGVtIGZyZWUgYnkgc2V0dGluZyBgc2NhbGVzID0gJ2ZyZWUnYCBidXQgdGhpcyB3YXkgaXQgY291bGQgYmUgaGFyZGVyIHRvIGNvbXBhcmUgYmV0d2VlbiBncm91cHMuCgpgYGB7ciwgZGVwZW5kc29uPSdtcGcnfQojIEZhY2V0IHdyYXAgd2l0aCBjb21tb24gc2NhbGVzCmcgKwogIGZhY2V0X3dyYXAoIH4gY2xhc3MsIG5yb3cgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJod3kgdnMgZGlzcGwiLAogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IG1wZyIsCiAgICAgICBzdWJ0aXRsZSA9ICJHZ3Bsb3QyIC0gRmFjZXRpbmcgLSBNdWx0aXBsZSBwbG90cyBpbiBvbmUgZmlndXJlIikKCiMgRmFjZXQgd3JhcCB3aXRoIGZyZWUgc2NhbGVzCmcgKwogIGZhY2V0X3dyYXAoIH4gY2xhc3MsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGUgPSAiaHd5IHZzIGRpc3BsIiwKICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBtcGciLAogICAgICAgc3VidGl0bGUgPSAiR2dwbG90MiAtIEZhY2V0aW5nIC0gTXVsdGlwbGUgcGxvdHMgaW4gb25lIGZpZ3VyZSB3aXRoIGZyZWUgc2NhbGVzIikKYGBgCgojIyBGYWNldCBncmlkCgpUaGUgaGVhZGluZ3Mgb2YgdGhlIG1pZGRsZSBhbmQgYm90dG9tIHJvd3MgdGFrZSB1cCBzaWduaWZpY2FudCBzcGFjZS4gVGhlIGBmYWNldF9ncmlkKClgIHdvdWxkIGdldCByaWQgb2YgaXQgYW5kIGdpdmUgbW9yZSBhcmVhIHRvIHRoZSBjaGFydHMuIFRoZSBtYWluIGRpZmZlcmVuY2Ugd2l0aCBgZmFjZXRfZ3JpZGAgaXMgdGhhdCBpdCBpcyBub3QgcG9zc2libGUgdG8gY2hvb3NlIHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucyBpbiB0aGUgZ3JpZC4KCmBgYHtyLCBkZXBlbmRzb249J21wZyd9CmcxIDwtIGcgKwogIGZhY2V0X2dyaWQobWFudWZhY3R1cmVyIH4gY2xhc3MpCmcxCmBgYAoKTGV0J3MgbWFrZSBvbmUgbW9yZSB0byB2YXJ5IGJ5IGN5bGluZGVyLgoKYGBge3IsIGRlcGVuZHNvbj0nbXBnJ30KZzIgPC0gZyArCiAgZmFjZXRfZ3JpZChjeWwgfiBjbGFzcykKZzIKYGBgCgojIyBDb21iaW5pbmcgbXVsdGlwbGUgcGxvdHMgaW50byBhIHNpbmdsZSBncmFwaAoKSXQgaXMgcG9zc2libGUgdG8gbGF5b3V0IGJvdGggdGhlc2UgY2hhcnRzIGluIHRoZSBzYW1lIGdyYXBoIHVzaW5nIHRoZSBgZ3JpZEV4dHJhYCBwYWNrYWdlLgoKYGBge3IsIGRlcGVuZHNvbj0nbXBnJ30KbGlicmFyeShncmlkRXh0cmEpCmdyaWQuYXJyYW5nZShnMSwgZzIsIG5jb2wgPSAyKQpgYGAKCiMgTW9kaWZ5aW5nIHBsb3QgYmFja2dyb3VuZCwgbWFqb3IgYW5kIG1pbm9yIGF4aXMKCiMjIEhvdyB0byBjaGFuZ2UgdGhlIHBsb3QgYmFja2dyb3VuZAoKYGBge3IsIGRlcGVuZHNvbj0nbXBnJ30KIyBjaGFuZ2UgcGxvdCBiYWNrZ3JvdW5kIGVsZW1lbnRzCmcgKwogIHRoZW1lKAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ2toYWtpJyksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImJ1cmx5d29vZCIsIHNpemUgPSAxLjUpLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZSgKICAgICAgY29sb3VyID0gInRvbWF0byIsCiAgICAgIHNpemUgPSAuMjUsCiAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIKICAgICksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZSgKICAgICAgY29sb3VyID0gImRhcmtvcmFuZ2UiLAogICAgICBzaXplID0gMS41LAogICAgICBsaW5lZW5kID0gImJ1dHQiCiAgICApLAogICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZGFya29yYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMS41KQogICkgKwogIGxhYnModGl0bGUgPSAiTW9kaWZpZWQgQmFja2dyb3VuZCIsCiAgICAgICBzdWJ0aXRsZSA9ICJIb3cgdG8gQ2hhbmdlIE1ham9yIGFuZCBNaW5vciBncmlkLCBBeGlzIExpbmVzLCBObyBCb3JkZXIiKQoKIyBjaGFuZ2UgcGxvdCBtYXJnaW5zCmcgKwogIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInNhbG1vbiIpLAogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDIsIDIsIDEsIDEpLCAiY20iKSkgKyAgIyB0b3AsIHJpZ2h0LCBib3R0b20sIGxlZnQKICBsYWJzKHRpdGxlID0gIk1vZGlmaWVkIEJhY2tncm91bmQiLCAKICAgICAgIHN1YnRpdGxlID0gIkhvdyB0byBDaGFuZ2UgUGxvdCBNYXJnaW4iKSAgCmBgYAoKIyMgUmVtb3ZlIG1ham9yIGFuZCBtaW5vciBncmlkLCBjaGFuZ2UgYm9yZGVyLCBheGlzIHRpdGxlLCB0ZXh0LCBhbmQgdGlja3MKCkJ5IHVzaW5nIHRoZSBgZWxlbWVudF9ibGFuaygpYCB0eXBlLCB3ZSBjYW4gdHVybiBvZmYgYW55IG9yIGFsbCBvZiB0aGVzZSBjb21wb25lbnRzLgoKYGBge3IsIGRlcGVuZHNvbj0nYmFzZSd9CmcgKwogIHRoZW1lKAogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIGxhYnModGl0bGUgPSAiTW9kaWZpZWQgQmFja2dyb3VuZCIsCiAgICAgICBzdWJ0aXRsZSA9ICJIb3cgdG8gcmVtb3ZlIG1ham9yIGFuZCBtaW5vciBheGlzIGdyaWQsIGJvcmRlciwgYXhpcyB0aXRsZSwgdGV4dCBhbmQgdGlja3MiKSAKYGBgCgojIEFja25vd2xlZGdlbWVudHMgey50b2MtaWdub3JlfQoKKiBNb2RpZmllZCBmcm9tIFNlbHZhIFByYWJoYWthcmFuJ3MgWypUaGUgQ29tcGxldGUgZ2dwbG90MiBUdXRvcmlhbCAtIFBhcnQgMiB8IEhvdyBUbyBDdXN0b21pemUgZ2dwbG90MiAoRnVsbCBSIGNvZGUpKl0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Db21wbGV0ZS1HZ3Bsb3QyLVR1dG9yaWFsLVBhcnQyLUN1c3RvbWl6aW5nLVRoZW1lLVdpdGgtUi1Db2RlLmh0bWwpLCBsaWNlbnNlZCB1bmRlciB0aGUgW0NDIEJZLU5DIDMuMCBDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2VdKGh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy8zLjAvKS4KCiMgU2Vzc2lvbiBJbmZvIHsudG9jLWlnbm9yZX0KCmBgYHtyIGNoaWxkPSdfc2Vzc2lvbmluZm8uUm1kJ30KYGBgCgoK
This work is licensed under the CC BY-NC 4.0 Creative Commons License.